﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Engine
{

    /**
     * this class represent 2-d triangular lattice.
     * */
    public class Triangular : Lattice
    {
        /**This array of point represent our triangular lattice.*/
        private int[,] grid;

        private const int dimension = 2;

        private int xLenght;

        private int yLenght;

        private int cuttedPoints;

        private int cuttedBonds;

        private IList<Point> pointList;

        private Bonds bonds;
        /**This instanse used for genetating random numners.*/
        private Random random;

        /**Default constructor */
        public Triangular()
            : base()
        {
            xLenght = 9;
            yLenght = 9;
            grid = new int[xLenght + 2, yLenght + 2];
            random = new Random();
            fillArray(grid);
            fillPointList();
            fillBondsList();
        }

        /** fill bonds list. */
        private void fillBondsList()
        {
            bonds = new Bonds();
            for (int y = 1; y < yLenght + 1; y++)
            {
                //go throw all internal points of lattice.
                for (int x = 0; x < xLenght + 2; x++)
                {
                    if (y % 2 != 0)
                    {
                        if (x % 2 != 0)
                        {
                            Bond bondLB = new Bond(x, y, x - 1, y - 1);
                            Bond bondRB = new Bond(x, y, x + 1, y - 1);
                            Bond bondLT = new Bond(x, y, x - 1, y + 1);
                            Bond bondRT = new Bond(x, y, x + 1, y + 1);
                            bonds.Add(bondLB);
                            bonds.Add(bondLT);
                            bonds.Add(bondRB);
                            bonds.Add(bondRT);
                            if (x + 1 != xLenght + 1)
                            {
                                Bond horizontalBond = new Bond(x, y, x + 2, y);
                                bonds.Add(horizontalBond);
                            }
                        }
                    }
                    else
                    {
                        if (x % 2 == 0 && x != xLenght + 1)
                        {
                            Bond horizontalBond = new Bond(x, y, x + 2, y);
                            bonds.Add(horizontalBond);
                        }
                    }
                }
            }
        }

        /** fill point list. */
        private void fillPointList()
        {
            //we do it for reset state of that property.
            pointList = new List<Point>();
            //We add only internal points.
            for (int y = 1; y < yLenght + 1; y++)
            {
                for (int x = 0; x < xLenght + 2; x++)
                {
                    if (y % 2 == 0)
                    {
                        if (x % 2 == 0)
                        {
                            Point point = new Point(x, y);
                            pointList.Add(point);
                        }
                    }
                    else
                    {
                        if (x % 2 != 0)
                        {
                            Point point = new Point(x, y);
                            pointList.Add(point);
                        }
                    }
                }
            }
        }

        /**prepare array for representing lattice. 
         * 1 - working bond 
         * 0 - cutted bond
         * null - no bond found with such coordinates.
         */
        private void fillArray(int[,] arr)
        {
            int xL = arr.GetLength(0);
            int yL = arr.GetLength(1);
            for (int y = 0; y < yL; y++)
            {
                for (int x = 0; x < xL; x++)
                {
                    if (y % 2 == 0)
                    {
                        if (x % 2 == 0)
                        {
                            arr[x, y] = 1;
                        }
                    }
                    else
                    {
                        if (x % 2 != 0)
                        {
                            arr[x, y] = 1;
                        }
                    }
                }
            }
        }

        public override int CuttedPoints
        {
            get { return cuttedPoints; }
        }

        public override int WorkingPoints
        {
            get { return pointList.Count; }
        }

        public override int CuttedBonds
        {
            get { return cuttedBonds; }
        }

        public override int WorkingBonds
        {
            get { return bonds.Count; }
        }

        public override double Conductance
        {
            get { throw new NotImplementedException(); }
        }

        public override void CutBond()
        {
            int size = bonds.Count;
            int index = random.Next(size);
            //what i should do with this bond
            Bond bondToCut = bonds[index];
            //!TODO - stupid logic at this plaxe 
            bonds.Remove(bondToCut);
            cuttedBonds++;
        }

        public override void CutPoint()
        {
            int size = pointList.Count;
            int index = random.Next(size);
            Point pointToCut = (Point)pointList[index];
            pointList.Remove(pointToCut);
            grid[pointToCut.X, pointToCut.Y] = 0;
            cuttedPoints++;
        }

        /**Actual method that contain algorihm of 
         * determination is our lattice conduct. 
         */
        public override bool IsConduct()
        {
            int[,] check = new int[xLenght + 2, yLenght + 2];
            Array.Copy(grid, check, (xLenght + 2) * (yLenght + 2));
            for (int y = 0; y < yLenght + 1; y++)
            {
                if (y == 0)
                {
                    for (int x = 0; x < xLenght + 2; x++)
                    {
                        if (x % 2 == 0)
                        {
                            //we don't need check there because
                            //this points is never removed
                            check[x, y] = 2;
                        }
                    }
                }

                //seriesPercolationThreashold we start horizontal walk in direct direction
                for (int x = 0; x < xLenght + 2; x++)
                {
                    if (check[x, y] == 2)
                    {
                        if (y % 2 == 0)
                        {
                            if (x % 2 == 0)
                            {
                                if (x == xLenght + 1)
                                {
                                    //do nothing
                                }
                                else
                                {
                                    if (isConnect(check, x, y, x + 2, y))
                                    {
                                        check[x + 2, y] = 2;
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (x % 2 != 0)
                            {
                                if (x == xLenght)
                                {
                                    //do nothing
                                }
                                else
                                {
                                    if (isConnect(check, x, y, x + 2, y))
                                    {
                                        check[x + 2, y] = 2;
                                    }
                                }
                            }
                        }
                    }
                }
                //then we start horizontal walk in backward direction
                for (int x = xLenght + 1; x >= 0; x--)
                {
                    if (check[x, y] == 2)
                    {
                        if (y % 2 == 0)
                        {
                            if (x % 2 == 0)
                            {
                                if (x == 0)
                                {
                                    //do nothing
                                }
                                else
                                {
                                    if (isConnect(check, x, y, x - 2, y))
                                    {
                                        check[x - 2, y] = 2;
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (x % 2 != 0)
                            {
                                if (x == 1)
                                {
                                    //do nothing
                                }
                                else
                                {
                                    if (isConnect(check, x, y, x - 2, y))
                                    {
                                        check[x - 2, y] = 2;
                                    }
                                }
                            }
                        }
                    }
                }

                //the third walk is need to find conncetion with next row of points
                for (int x = 0; x < xLenght + 2; x++)
                {
                    if (check[x, y] == 2)
                    {
                        if (y % 2 == 0)
                        {
                            if (x % 2 == 0)
                            {
                                if (x == 0)
                                {
                                    if (isConnect(check, x, y, x + 1, y + 1))
                                    {
                                        check[x + 1, y + 1] = 2;
                                    }
                                }
                                else if (x == xLenght + 1)
                                {
                                    if (isConnect(check, x, y, x - 1, y + 1))
                                    {
                                        check[x - 1, y + 1] = 2;
                                    }
                                }
                                else
                                {
                                    if (isConnect(check, x, y, x - 1, y + 1))
                                    {
                                        check[x - 1, y + 1] = 2;
                                    }
                                    if (isConnect(check, x, y, x + 1, y + 1))
                                    {
                                        check[x + 1, y + 1] = 2;
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (x % 2 != 0)
                            {
                                if (isConnect(check, x, y, x - 1, y + 1))
                                {
                                    check[x - 1, y + 1] = 2;
                                }
                                if (isConnect(check, x, y, x + 1, y + 1))
                                {
                                    check[x + 1, y + 1] = 2;
                                }
                            }
                        }
                    }
                }
            }
            //then we need check the last row to find is our lattice conduct electricity
            bool isConduct = false;
            
            for (int x = 0; x < xLenght + 2; x++ )
            {
                if(check[x, yLenght + 1] == 2)
                {
                    isConduct = true;
                    break; //go out from this loop. 
                }
            }
            return isConduct;
        }

        /** check is two point connect with each other */
        private bool isConnect(int[,] arr, int x1, int y1, int x2, int y2)
        {
            if (bonds.isConduct(x1, y1, x2, y2) && arr[x2, y2] == 1)
            {
                return true;
            }
            return false;
        }



        /** Reset internal state of lattice. It include 
         * unbderline array and two ist of bonds and points 
         * correspondly. */
        public override void Reset()
        {
            fillArray(grid);
            fillBondsList();
            fillPointList();
            cuttedBonds = 0;
            cuttedPoints = 0;
        }

        public override int Dimension
        {
            get { return dimension; }
        }

        public override int X
        {
            get
            {
                return xLenght;
            }
            set
            {
                xLenght = value;
                grid = new int[xLenght + 2, yLenght + 2];
                Reset();
            }
        }

        public override int Y
        {
            get
            {
                return yLenght;
            }
            set
            {
                yLenght = value;
                grid = new int[xLenght + 2, yLenght + 2];
                Reset();
            }
        }

        public override int Z
        {
            get
            {
                throw new NotImplementedException("this lattice doesn't support this property.");
            }
            set
            {
                //do nothing
            }
        }

        public override IList<Point> Points
        {
            get { return pointList; }
        }

        public override IList<Bond> Bonds
        {
            get { return bonds.BondList; }
        }
    }
}
